package cn.xinfei.xdecision.engine.runner.executor.strategy.node;

import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.datax.datamanage.Field;
import cn.xinfei.xdecision.common.model.enginex.model.*;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.datax.datamanage.FieldService;
import cn.xinfei.xdecision.common.utils.constant.runner.ParamTypeConst;
import cn.xinfei.xdecision.common.utils.enums.enginex.ConditionValueTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.util.runner.JevalUtil;
import cn.xinfei.xdecision.common.utils.util.runner.jeval.EvaluationException;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.AmmunitionDepot;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.XRunner;
import cn.xinfei.xdecision.engine.runner.executor.handler.NodeOutputHandler;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.executor.strategy.EngineNodeRunner;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static cn.xinfei.xdecision.constant.EngineResultEnum.EXCEPTION_NODE_NULL_END;

/**
 * 决策选项节点
 */
@XRunner
@Service
@Slf4j
public class DecisionOptions extends EngineNodeRunner {

    @Autowired
    private FieldService fieldService;

    @Resource
    private NodeOutputHandler nodeOutputHandler;

    @Override
    public String getServiceName() {
        return EngineNodeRunner.PRE + NodeTypeEnum.DECISION.getType();
    }

    @Override
    public void before(NodeHandler nodeHandler) {

    }

    @Override
    public Map<String, FieldInfoVo> getInputFieldInfo(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        String engineCode = engineNode.getEngineCode();
        if (engineCode.equalsIgnoreCase("jcl_20230526000001")) {
            System.out.println();
        }

        String nodeCode = engineNode.getNodeCode();
        Map<String, FieldInfoVo> fieldVos = new HashMap<>();
        try {
            Set<String> fieldCodes = new HashSet<>();
            String nodeNodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeNodeJson)) {
                log.error("DecisionOptions node json is null");
                return fieldVos;
            }

            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);

            List<NodeJsonConditions> conditions = nodeJsonModel.getConditions();
            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            if (org.springframework.util.CollectionUtils.isEmpty(conditions) == false) {
                conditions.forEach(condition -> {
                    List<NodeFormulas> formulas = condition.getFormulas();
                    if (CollectionUtils.isNotEmpty(formulas)) {
                        for (NodeFormulas formula : formulas) {
                            fieldCodes.add(formula.getFieldCode());
                        }
                    }
                });
            }

            List<Field> fields = fieldService.queryByEns(fieldCodes);
            fields.forEach(field -> {
                String fieldCode = field.getFieldCode();
                FieldInfoVo value = fieldInfo.get(fieldCode);
                if (value != null) {
                    fieldVos.put(fieldCode, value);
                }
            });

        } catch (Exception e) {
            log.error("DecisionOptions,getInputFieldInfo:获取决策选项标异常,error={},engineCode={},nodeCode={}", e.getMessage(), engineCode, nodeCode, e);
        }
        return fieldVos;


//        log.info("获取决策选项节点指标 getNodeFieldName engineNode:{}", JsonUtils.toJsonString(engineNode));
//        Map<String, EngineNodeFieldVo> fieldVos = new HashMap<>();
//        return fieldVos;
    }

    @Override
    public Map<String, StrategyOutput> getOutputFieldInfo(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();

        Map<String, StrategyOutput> strategyOutputMap = super.getOutputFieldInfo(nodeHandler);
        try {
            Set<String> fieldCodes = new HashSet<>();
            String nodeScript = engineNode.getNodeScript();
            if (StringUtils.isEmpty(nodeScript)) {
                return strategyOutputMap;
            }

            NodeScriptModel nodeScriptModel = JSONObject.parseObject(nodeScript, NodeScriptModel.class);

            NodeOutPut nodeOutPut = nodeScriptModel.getOutput();
            if (null != nodeOutPut) {
                String fieldCode = nodeOutPut.getFieldCode();
                fieldCodes.add(fieldCode);
            }

            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();

            fieldCodes.forEach(fieldCode -> {
                FieldInfoVo value = fieldInfo.get(fieldCode);
                if (value != null) {
                    StrategyOutput output = new StrategyOutput();
                    output.setFieldCode(fieldCode);
                    output.setVarValueType(value.getValueType());
                    //固定输出， 此时ValueType并不一定会在后续过程中起作用，一定会在runNode中给他负值，但会存在赋值时报错的可能，比如类型不匹配，所以设置为常量，在值缺失的时候取默认值
                    output.setValueType(ConditionValueTypeEnum.CONSTANT.getCode());
                    output.setFieldValue(value.getVarDefaultValue());
                    output.setStage(value.getStage());
                    output.setFixed(true);
                    strategyOutputMap.put(fieldCode, output);
                } else {
                    log.warn("变量不存在,fieldCode={}", fieldCode);
                }
            });

        } catch (Exception e) {
            log.error("DecisionOptions,getOutputFieldInfo:获取决策选项标异常,error={},engineCode={},nodeCode={}", e.getMessage(), engineCode, nodeCode, e);
        }
        return strategyOutputMap;
    }

    /**
     *
     */
    @Override
    public StateTransmitModel runNode(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String json = engineNode.getNodeScript();
        if (StringUtils.isEmpty(json)) {
            log.error("DecisionOptions node json is null");
            return XDecisionContext.getContext().getStateContext().setEngineResultEnum(EXCEPTION_NODE_NULL_END);
        }
        NodeScriptModel nodeScriptModel = JSONObject.parseObject(json, NodeScriptModel.class);


        // 变量值转义
        Map<String, Integer> fieldTypeMap = nodeScriptModel.getFieldTypeMap();
        Map<String, Object> variablesMap = processResult(engineNode.getNodeJson(), fieldTypeMap);


        NodeOutPut output = nodeScriptModel.getOutput();

        // 默认值处理
        String dicisionResult = "";
        String defaultValue = output.getDefaultValue();
        if (StringUtils.isNotBlank(defaultValue)) {
            dicisionResult = defaultValue;
        }
        // 决策条件判断
        List<NodeScriptConditions> conditions = nodeScriptModel.getConditions();
        if (conditions != null && conditions.size() > 0) {
            for (int i = 0; i < conditions.size(); i++) {
                NodeScriptConditions nodeCondition = conditions.get(i);
                try {
                    String formula = nodeCondition.getFormulaScript();

                    boolean formulaResult = JevalUtil.evaluateBoolean(formula, variablesMap);
                    if (formulaResult) {
                        dicisionResult = nodeCondition.getResult();
                        break;
                    }
                } catch (EvaluationException e) {
                    log.error("决策条件判断异常，error={}", e.getMessage(), e);
                }
            }
        }


        String outputFieldCode = output.getFieldCode();
        //放入节点执行结果
        setOutput(nodeHandler, outputFieldCode, dicisionResult);

        log.info("执行决策选项节点，nodeCode={},executeResult={}", nodeCode, dicisionResult);
        //放入变量上下文
        if (null != dicisionResult) {
            //nothing to do
        }

        String nextNodeCodes = engineNode.getNextNodeCodes();
        return XDecisionContext.getContext().getStateContext().setNextNodeCode(nextNodeCodes);

    }

    private Map<String, Object> processResult(String nodeJson, Map<String, Integer> fieldTypeMap) {
        Map<String, Object> variablesMap = new HashMap<>();
        NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeJson, NodeJsonModel.class);
        List<NodeJsonConditions> cds = nodeJsonModel.getConditions();
        if (null != cds && cds.size() > 0) {
            for (int i = 0; i < cds.size(); i++) {
                NodeJsonConditions nodeJsonConditions = cds.get(i);
                List<NodeFormulas> formulas = nodeJsonConditions.getFormulas();
                if (CollectionUtils.isNotEmpty(formulas)) {
                    for (int j = 0; j < formulas.size(); j++) {
                        NodeFormulas nodeFormulas = formulas.get(j);
                        String fieldCode = nodeFormulas.getFieldCode();
                        Object valueByKey = AmmunitionDepot.getValueByKeyFromRemote(ParamTypeConst.VARIABLE, fieldCode, fieldCode);
                        if(null!=valueByKey) {
                            variablesMap.put(fieldCode, valueByKey);
                        }
                    }
                }
            }
        }

        Map<String, Object> result = AmmunitionDepot.convertVariables(fieldTypeMap, variablesMap);
        return result;
    }

    @Override
    public boolean terminalCondition(NodeHandler nodeHandler, Map<String, Object> nodeOutput, Map<String, Object> nodeInput) {
        Map<String, StrategyOutput> outputFields = nodeHandler.getOutputFields();
        if (null != outputFields) {
            List<StrategyOutput> strategyOutputs = new ArrayList<>(outputFields.values());
            try {
                Map<String, Object> customOutput = nodeOutputHandler.getOutput(nodeHandler.getCode(), strategyOutputs);
                Object result = customOutput.get("result");
                log.info("DecisionOptions...result===>{}", result);
                return Objects.nonNull(result) && result.toString().equalsIgnoreCase("reject");
            } catch (Exception e) {
                log.error("DecisionOptions...getNodeOutput fail.error={},", e.getMessage(), e);
                return false;
            }
        }
        return false;
    }

}
